C++
1.4k words
AVL树之前我们讲了二叉搜索树的相关内容,但是也了解到二叉搜索树有其自身的缺陷,就是当插入的元素有序或者接近有序,退化成单支树的时候,他的时间复杂度就会退化成O(N),因此我们需要对他进行优化,有两种,一种是AVL树,也就是平衡二叉树,另一种是红黑树,这里我们先介绍AVL树 概念在发现这个问题之后,两个俄罗斯数学家发明了解决这个问题的一种办法 当向二叉搜索树中插入节点之后,检查每个节点的左右子树高度只差绝对值不超过一,否则需要进行调整 这样就能降低树的高度,从而减少平均的搜索长度 一棵AVL树是空树或者有如下性质 左右子树都是AVL树 左右子树高度差(平衡因子)的绝对值不超过1 例如 节点12345678910111213141516template<class K, class V>struct AVLTreeNode { AVLTreeNode<K, V>* _left; // 左子树 AVLTreeNode<K, V>* _right; // 右子树 AVLTreeNode<K, V>* _parent; //...
1.6k words
构建对话式应用对话式应用的特点 交互性 使用自然语言,文本,声音来直接和应用互动,使用成本和学习成本低 智能化 能够通过对话式应用的载体较为全面的发挥出大语言模型的功能,例如逻辑、记忆、理解、生成 跨设备支持 对于不同种类的设备都能有很好的支持,例如手机、手表、网站、音响等,都能够落地展现出不同场景的支持能力 对话式应用的开发流程评估一个对话式应用的标准,一是要降低用户使用学习的门槛,二是要能够准确识别用户的输入和上下文语境,以此来生成高质量的结果 一般来说开发的过程有如下五个模块 基座模型选型 参数设计 系统提示词设计 示例设计 检索增强数据库设计 基座模型的选型我们使用飞桨大模型社区来进行应用构建,以我自己的项目为例 这里有五个基础的预制模型,可以查阅他们的区别和优势根据自己的需求选择使用,这里我选择LLaMA-13B 参数的调整除此之外有两个模型参数,一个是Temperature 我们需要根据自己的需求,温度越高,输出其他词的可能性也越高 第二个参数是TOP_P,他的意思就是采样和选择 这里我们就需要通过大量的实验来确定相应的参数选择了 除此之外还有一...
5.6k words
变量与环境我们之前已经实现了相当多的功能,例如波兰表达式的处理与计算,S表达式,Q表达式,我们甚至可以吧代码本身放到列表里,现在我们需要为MyLisp添加变量的功能了 不变性到目前位置,我们所添加的变量是不可变的,只是暂时我们还没有添加这个功能罢了 当我们在计算一个表达式的时候,他的基本逻辑是删除先前的事物(表达式),返回新的事物(结果) 所以我们的变量其实只是命名值的一种方式,给值分配一个名称,然后在需要的时候获取该值的副本 为了允许命名,我们需要创建一个结构体,存储命名中的所有内容的名称和值,我们称之为环境,当我们开始创建一个新的名称-表达式关系时,同时创建一个新的环境匹配他 在MyLisp中,如果我们给变量重新分配一个名称时,在底层上其实是把原先的对象删除,然后再新建一个,再分配名称,与C语言是有很大不同的 符号语法我们现在需要更新一下符号语法,以更好的适配变量命名 我们需要他灵活一点,可能匹配任何可能有效的符号,没有限制,正则表达式如下 /[a-zA-Z0-9_+\\-*\\/\\\\=<>!&]+/ 此规则允许符号是任何普通的 C 标识符字符 a-z...
4.2k words
Q表达式添加特性的一般步骤从这以后我们可以发现,给这个编程语言添加某个特性都有一定的步骤 语法:定义新的语法规则 表示:添加新的数据类型 解析:添加新的函数,正确处理AST 语义:添加新的函数,用于求值和操作 Q表达式这里我们会实现一个新的Lisp值类型,叫做Q表达式(Quoted Expression),与S表达式一样,也是Lisp表达式的一种,但他不受Lisp求值机制的作用 也就是说,当受到函数作用时,Q表达式不会被求值 因此我们可以使用Q表达式来存储和管理其他的Lisp值类型,例如数字,符号,S表达式 在添加Q表达式之后,我们需要定义一些操作来管理,类似于之前的数学操作,这些操作定义了Q表达式的具体行为 Q表达式与S表达式非常类似,不同的是Q表达式存在大括号内,S表达式存在小括号内 原生的Lisp并不支持Q表达式,他们通常使用宏来做到禁止表达式求值的功能。我们这里定义Q表达式来达到类似的效果 1234567891011121314151617181920212223242526// 创建解析器mpc_parser_t* Number = mpc_new("...
3.2k words
S表达式Lisp列表Lisp的程序代码也是数据形式的一种,这个结构被称之为S表达式,他存在一个Lisp列表中,我们需要用这个列表结构递归的表示出数字、操作符号和其他的列表,因此我们需要扩展MLval的内容,同时会重构之前写过的函数 我们需要将求值过程分为读取存入,求值两个过程 解析表达式这里就要由波兰表达式扩展到S表达式,因此首先需要更新一下语法解析器,这里笔者在创建的时候会有一部分内存泄漏的问题,但是重新写一遍这个解析器问题就消失了,也不会报错,可能是正则表达式的部分有出问题 除此之外我们需要把operator操作重命名为symbol,以便之后添加更多的操作符、变量、函数做准备 12345678910111213141516171819// 创建解析器mpc_parser_t* Number = mpc_new("number");mpc_parser_t* Symbol = mpc_new("symbol");mpc_parser_t* Sexpr = mpc_new("sexpr");mpc_parser_t* E...
2.7k words
波兰表达式计算AST树的结构在上一篇我们已经可以读取分析波兰表达式的内部结构了,这一篇我们将会对表达式进行计算,得到计算的结果 之前有提到过抽象语法树,AST,他是用来表示用户输入的的表达式的结构的,操作数和操作符这些需要被处理的数据都存在叶子节点上,而非叶子节点上存储的是遍历和求值的信息 在这里需要看一下mpc_ast_t的内部结构 1234567typedef struct mpc_ast_t { char* tag; char* contents; mpc_state_t state; int children_num; struct mpc_ast_t** children;} mpc_ast_t; tag表示节点内容之前的标签,他表示解析这个节点用到的规则,例如expr|number|regex contents包含了节点的具体内容,例如add、(,对于非叶子节点,这个内容为空 state表示这个节点所处的状态,例如行数和列数,这里不会用到 最后两个成员就是表示树的结构了,这里为了支持多叉树,采用指针数组的方式记录 运算符与数...
2.5k words
编程语言编程语言是类似于自然语言的,虽然我们是自然而然就学会了自己的母语,但实际上自然语言是建立在递归和重复的子结构之上的 在语文的学习过程中我们会学到一些语法,例如名词、动词、谓语、连词 他的结构体现在,例如谓语可以是形容词,是动词,是副词加动词 他的递归体现在,主语可以是名词,可以是形容词加名词,甚至是一个句子,再由这个主语去构成另一个句子 这些说白了是一种有限的规则,而由这些优先的规则延申出来的就是无限的句子 为了定义我们的编程语言(MyLisp),我们首先需要正确解析用户按照语法规则写的代码,因此我们就需要编写一个语法解析器,用于判断用户的输入是否合法,并且产生解析后的内部表示 但是这一步十分繁琐且枯燥,我们使用mpc库来帮助我们完成这个工作,这个库是一个解析器组合子库,可以为任何语言编写语法解析器,他极大的简化了原本枯燥无聊的工作,仅仅编写高层的抽象语法规则即可 mpc库的项目地址,非常感谢Daniel Holden大佬!! 模拟自然语言下面我们将使用mpc的一些函数,尝试描述英语的部分语法,熟悉mpc的使用步骤,更多的函数请移步项目主页 我们只采用最基础的语法,以后如...
3.3k words
生成式AI、提示词工程和零代码应用开发人工智能概念 这里我们逐一解释这些概念 AI是计算机学科下的一个分支学科,旨在使用计算机使之能像人类一样学习和思考问题 机器学习是AI的一个子集,他的一个重要特点就是不需要人去做显示编程(不用手撕函数),让计算机自行学习迭代总结规律,做出预测和决策 机器学习机器学习下就有很多分支了,例如监督学习、无监督学习、强化学习 监督学习目标是学习原始数据和标签之间的映射关系,他的一个特点就是是给定了原始数据和标签,让计算机自行总结归纳,他的经典的应用有对事物的分类、回归预测等 无监督学习就是相对于监督学习,他只给原始数据而不给定标签,让计算机自行发现数据中的模式或规律,他的应用有聚类(就是分组)、降维、异值检测、自编码器(大语言模型中用到)、自监督学习等 强化学习就是让模型在环境中采取行动,不断尝试,让他在环境中获得最大奖励的手段,类似于人类学习的模式,趋利避害,例如Alpha Go 深度学习深度学习具有这三类学习的一些特点,也有自己的模式,属于都有点但都不是的状态 关于深度学习的核心就是两个词,一是连接,二是深度 连接的意思是说,他采用了类似...